/* 
 *  FileName: sec_dev.c - A second dev driver.
 *
 *  Author: He Jingbo   jbhe@isoftstone.com
 *
 *  Date: Wed 12 Jan 2011 12:53:23 PM CST
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#include "sec_dev.h"

static int sec_dev_major = SEC_DEV_MAJOR;
struct sec_dev *sec_devp;       /* 设备结构体指针 */

/* 定时器处理函数 */
static void sec_timer_handle(unsigned long arg)
{
    mod_timer(&sec_devp->s_timer, jiffies + HZ);
    atomic_inc(&sec_devp->counter);

    printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
}

/* 文件打开函数 */
int sec_open(struct inode *inode, struct file *filp)
{
    /* 初始化定时器 */
    init_timer(&sec_devp->s_timer);
    sec_devp->s_timer.function  = &sec_timer_handle;
    sec_devp->s_timer.expires   = jiffies + HZ;

    add_timer(&sec_devp->s_timer);      /* 添加(注册)定时器 */
    atomic_set(&sec_devp->counter, 0);   /* 计数器清零 */

    return 0;
}

/* 文件释放函数 */
int sec_release(struct inode *inode, struct file *filp)
{
    /* 注销定时器 */
    del_timer(&sec_devp->s_timer);

    return 0;
}

/* 读函数 */
static ssize_t sec_read(struct file *filp, char __user *buf, 
                    size_t count, loff_t *ppos)
{
    int counter;

    counter = atomic_read(&sec_devp->counter);
    if(put_user(counter, (int *)buf))
    {
        return -EFAULT;
    }
    else
    {
        return sizeof(unsigned int);
    }

}

/* 文件操作结构体 */
static const struct file_operations sec_fops =
{
    .owner      = THIS_MODULE,
    .open       = sec_open,
    .read       = sec_read,
    .release    = sec_release,
};

/* 初始化并注册cdev */
static void sec_setup_cdev(struct sec_dev *dev, int index)
{
    int err, devno;
    
    devno = MKDEV(sec_dev_major, index);
    cdev_init(&dev->cdev, &sec_fops);
    dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&dev->cdev, devno, 1);
    if(err)
    {
        printk(KERN_NOTICE "Error %d adding LED%d", err, index);
    }
}

/* 设备驱动模块加载函数 */
int sec_init(void)
{
    int ret;
    dev_t devno;

    devno = MKDEV(sec_dev_major, 0);
    
    /* 申请设备号 */
    if(sec_dev_major)
    {
        ret = register_chrdev_region(devno, 1, "sec_dev");
    }
    else
    {
        ret = alloc_chrdev_region(&devno, 0, 1, "sec_dev");
        sec_dev_major = MAJOR(devno);
    }
    if(ret < 0)
    {
        return ret;
    }

    /* 动态申请设备结构内存 */
    sec_devp = kmalloc(sizeof(struct sec_dev), GFP_KERNEL);
    if(!sec_devp)
    {
        /* 申请失败 */
        ret = -ENOMEM;
        goto fail_malloc;
    }
    memset(sec_devp, 0, sizeof(struct sec_dev));
    sec_setup_cdev(sec_devp, 0);
    return 0;

fail_malloc:
    unregister_chrdev_region(devno, 1);
    return ret;
}

/* 模块卸载函数 */
void sec_exit(void)
{
    cdev_del(&sec_devp->cdev);      /* 注销cdev */
    kfree(sec_devp);                /* 释放设备结构体内存 */
    unregister_chrdev_region(MKDEV(sec_dev_major, 0), 1);       /* 释放设备号 */
}

module_init(sec_init);
module_exit(sec_exit);
module_param(sec_dev_major, int, S_IRUGO);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("He Jingbo <jbhe@isoftstone.com>");
